//
// Created by mio on 25-5-13.
//

#include "SelectTaskScheduler.h"

#include <forward_list>

SelectTaskScheduler::SelectTaskScheduler(int id) : TaskScheduler(id)
{
    FD_ZERO(&fd_read_backup_);
    FD_ZERO(&fd_write_backup_);
    FD_ZERO(&fd_exp_backup_);
}

SelectTaskScheduler::~SelectTaskScheduler() {
}

void SelectTaskScheduler::UpdateChannel(ChannelPtr channel)
{
    std::lock_guard<std::mutex> lock(mutex_);

    SOCKET socket = channel->GetSocket();
    // 查询该socket是否已经存在
    if (channels_.find(socket) != channels_.end()) // 存在
    {
        if (channel->IsNoneEvent()) // 不关心任何事件就移除
        {
            // 重置该事件
            is_fd_read_reset_ = true;
            is_fd_write_reset_ = true;
            is_fd_exp_reset_ = true;
            channels_.erase(socket);
        }
        else
        {
            is_fd_read_reset_ = true;
            is_fd_write_reset_ = true;
        }
    }
    else // 没有该socket, 就去判断事件类型
    {
        if (!channel->IsNoneEvent())
        {
            // 添加该socket
            channels_.emplace(socket, channel);
            // 重置事件
            is_fd_read_reset_ = true;
            is_fd_write_reset_ = true;
            is_fd_exp_reset_ = true;
        }
    }
}

void SelectTaskScheduler::RemoveChannel(ChannelPtr channel)
{
    std::lock_guard<std::mutex> lock(mutex_);

    SOCKET socket = channel->GetSocket();
    if (channels_.find(socket) != channels_.end())
    {
        // 重置该事件
        is_fd_read_reset_ = true;
        is_fd_write_reset_ = true;
        is_fd_exp_reset_ = true;
        channels_.erase(socket);
    }
}

bool SelectTaskScheduler::HandleEvent(int timeout)
{
    fd_set fd_read;
    fd_set fd_write;
    fd_set fd_exp;
    FD_ZERO(&fd_read);
    FD_ZERO(&fd_write);
    FD_ZERO(&fd_exp);
    bool fd_read_reset = false;
    bool fd_write_reset = false;
    bool fd_exp_reset = false;

    if (is_fd_read_reset_ || is_fd_write_reset_ || is_fd_exp_reset_)
    {
        if (is_fd_exp_reset_) { maxfd_ = 0; }

        std::lock_guard<std::mutex> lock(mutex_);
        for (auto iter : channels_)
        {
            int event = iter.second->GetEvents();
            SOCKET fd = iter.second->GetSocket();

            if (is_fd_read_reset_ && (event & EVENT_IN))
            {
                FD_SET(fd,&fd_read);
            }

            if (is_fd_write_reset_ && (event & EVENT_OUT))
            {
                FD_SET(fd,&fd_write);
            }

            if (is_fd_read_reset_)
            {
                FD_SET(fd,&fd_exp);
                if (fd > maxfd_) maxfd_ = fd;
            }
        }

        fd_read_reset = is_fd_read_reset_;
        fd_write_reset = is_fd_write_reset_;
        fd_exp_reset = is_fd_exp_reset_;

        //将这些重置成员变量置为false;
        is_fd_read_reset_ = false;
        is_fd_write_reset_ = false;
        is_fd_exp_reset_ = false;
    }

    // 拷贝fd_set
    if (fd_read_reset)
    {
        FD_ZERO(&fd_read_backup_);
        memcpy(&fd_read_backup_, &fd_read, sizeof(fd_set));
    }
    else
    {
        memcpy(&fd_read,&fd_read_backup_,sizeof(fd_set));
    }

    if (fd_write_reset)
    {
        FD_ZERO(&fd_write_backup_);
        memcpy(&fd_write_backup_, &fd_write, sizeof(fd_set));
    }
    else
    {
        memcpy(&fd_write, &fd_write_backup_, sizeof(fd_set));
    }

    if (fd_exp_reset)
    {
        FD_ZERO(&fd_exp_backup_);
        memcpy(&fd_exp_backup_, &fd_exp, sizeof(fd_set));
    }
    else
    {
        memcpy(&fd_exp, &fd_exp_backup_, sizeof(fd_set));
    }

    int ret = 0;
    if (timeout == -1)
    {
        ret = select(maxfd_ + 1, &fd_read, &fd_write, &fd_exp, nullptr);
    }
    else
    {
        timeval tv = { 0, timeout * 1000 }; // 如果是空，select不会返回，如果是0会立即返回，如果大于0会等待这个时间在返回
        ret = select(maxfd_ + 1, &fd_read, &fd_write, &fd_exp, &tv);
    }

    if (ret < 0) { return false; }

    std::forward_list<std::pair<ChannelPtr,int>> event_list; //存放channel和事件
    if (ret > 0)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        for (auto iter : channels_) // 遍历这个映射表，来获取这些发生事件再去统一处理
        {
            int events = 0;
            SOCKET socket = iter.second->GetSocket();

            //判断事件类型
            if (FD_ISSET(socket, &fd_read)) { events |= EVENT_IN; }
            if (FD_ISSET(socket, &fd_write)) { events |= EVENT_OUT; }
            if (FD_ISSET(socket, &fd_exp)) { events |= EVENT_HUP; }
            if (events != 0) { event_list.emplace_front(iter.second, events); } // 有事件发生，就去添加到这个event_list
        }
    }

    //处理事件
    for (auto iter : event_list)
    {
        iter.first->HandleEvent(iter.second);
    }

    return true;
}
